import java.awt.Cursor;

import processing.core.PApplet;
import processing.core.PImage;

@SuppressWarnings("serial")
public class Carcassonne extends PApplet {

	public static int NUMPLAYERS = 4; // max players = 4

	static final boolean DEBUGDRAW = true;	

	// interface params
	public static final int WIDTH = 600;
	public static final int HEIGHT = 700;

	// screens
	public TitleScreen titleScreen;
	public InfoOverlay infoOverlay;
	public FinaleScreen finaleScreen;
	// public HelpScreen helpScreen;
	// public GameScreen gameScrene; // ?

	// state
	public int TITLE = 0;
	public int GAME = 1;
	public int state = TITLE;

	// camera sets the upper lefthand coordinate (pixel coordinates) in the view panel
	public int camX = 0, camY = 0;

	// move viewpoint
	public int dragPointX, dragPointY;
	public boolean isDragging = false;

	public Cursor defaultCursor;
	public Cursor dragCursor;

	// tile image constants
	static public int IMGWIDTH;
	static public int IMGHEIGHT;

	Game g; // current game (if ==null then no game is currently being played)
	Board b;
	Tile nextTile; // the tile the player needs to place

	// the theme to use
	Theme theme;

	// rotation animation
	boolean isRotating = false;
	static final float rotationSpeed = 0.1f;
	float partialRotation = 0;
	
	/**************************/

	public void setup()
	{
		size(WIDTH,HEIGHT);
		dragCursor = new Cursor(Cursor.HAND_CURSOR);
		defaultCursor = new Cursor(Cursor.DEFAULT_CURSOR);
		setCursor(defaultCursor);

		theme = new Theme(this);
		textFont(theme.font,32);
		IMGWIDTH = theme.getTileWidth();
		IMGHEIGHT = theme.getTileHeight();

		Tile.tw = IMGWIDTH;
		Tile.th = IMGHEIGHT;
		
		// set up screens	
		titleScreen = new TitleScreen(this);
		infoOverlay = new InfoOverlay(this);
		finaleScreen = new FinaleScreen(this);
	}

	public void startGame()
	{
		g = new Game(NUMPLAYERS,this);
		b = g.board;
		state = GAME;
		
		// the first piece has been placed, so cache its world coords
		Tile t = g.lastTilePlaced();
		t.x = t.i * IMGWIDTH;
		t.y = t.j * IMGHEIGHT;
		
		g.start();
		nextTile = g.nextTile();

		centerCamera();
	}

	public void draw()
	{
		// theme.setBackgroundFill(this);
		background(255);

		if (state==TITLE)
		{
			titleScreen.draw();
		}
		else if (g.state == Game.FINISHED)
		{
			finaleScreen.draw();
		}
		else if (state==GAME)
		{
			tint(255);
			drawPlayers();
			drawBoard(b);
			
			if (g.currentPlayer instanceof AIPlayer)
			{
				infoOverlay.setMessage("AI playing. Wait...");
			}
			else
			{
				if (g.playing_state==Game.PLAYING_TILE)
				{
					if (nextTile!=null)
					{
						drawCurrentPlayingTile();					
					}

					fill(255);
					infoOverlay.setMessage("Player " + g.whoseTurnIsIt + ": playing a tile...");

				} // end playingtile
				else if (g.playing_state==Game.PLAYING_MEEPLE)
				{
					drawCurrentPlayingMeeple();				

					infoOverlay.setMessage("Player " + g.whoseTurnIsIt + ": playing a meeple...");
					// draw blah
				}		
				else if (g.playing_state==Game.END_TURN)
				{
					// do something for the end turn screen,
					// e.g., an animation of the meeples being removed,
					// score increasing etc...
					// but keep it simple, quick, and quiet

					// atm just draw a placeholder
					fill(255);
					infoOverlay.setMessage("End of turn. Click...");				
				}

			}
			
			infoOverlay.draw();
		}		
	}	

	public void beginHotseatPlayerTurn()
	{
		nextTile = g.nextTile();
	}

	public void drawCurrentPlayingTile()
	{
		if (isRotating)
		{
			partialRotation += rotationSpeed;
			if (partialRotation > 1)
			{
				isRotating = false;
				partialRotation = 1;
			}
		}

		// snap mouse to nearest grid point
		int si = bmouseX() / IMGWIDTH;
		int sj = bmouseY() / IMGHEIGHT;
		if (b.isValidCoordinate(si,sj))
		{			
			int sx = bmouseX(), sy = bmouseY();
			if (b.get(si, sj)==null)
			{
				sx = (int)(IMGWIDTH * (si + 0.5));
				sy = (int)(IMGHEIGHT * (sj + 0.5));

				tint(230+25*sin(3*(float) (millis()/1000.0)),200);
			}
			else
			{
				tint(255,100);
			}
			
			pushMatrix();
			//translate(PLAYERSIDEBARWIDTH,0);
			if (isRotating)
				drawTile(nextTile,realToCamX(sx-IMGWIDTH/2.f),realToCamY(sy-IMGHEIGHT/2.f),(nextTile.getRotation()-1) + partialRotation);
			else
				drawTile(nextTile,realToCamX(si*IMGWIDTH),realToCamY(sj*IMGHEIGHT));
			popMatrix();
			//if (!b.isValidLocation(si, sj, nextTile))
			//	image(imgNo,mouseX+10,mouseY-10);					
		}
	}

	public void drawCurrentPlayingMeeple()
	{
		fill(255);
		// snap meeple to closest location
		Tile lastTile = g.lastTilePlaced();				
		Tile.MeepleLocation cloc = lastTile.findClosestAvailableMeepleLocation(bmouseX(),bmouseY());
		if (cloc!=null) 
		{
			if (cloc.distanceFromPoint(bmouseX(),bmouseY()) < IMGWIDTH/2)
			{
				//translate(PLAYERSIDEBARWIDTH,0);						
				float mx = cloc.wx-theme.getMeepleImage(g.whoseTurnIsIt).width/2.f;
				float my = cloc.wy-theme.getMeepleImage(g.whoseTurnIsIt).height/2.f;
				image(theme.getMeepleImage(g.whoseTurnIsIt),realToCamX(mx),realToCamY(my));
			}
		}
		else
		{
			tint(255,100);
			image(theme.getMeepleImage(g.whoseTurnIsIt),mouseX-10,mouseY-10);
		}
	}

	public void mousePressed()
	{
		if (state==TITLE)
		{
			boolean handled = titleScreen.mousePressed(mouseX,mouseY);
			if (handled)
			{
				startGame();
			}
			return;
		}
		
		// moving the window...
		if (keyPressed && keyCode==CONTROL)
		{
			dragPointX = mouseX - camX;
			dragPointY = mouseY - camY;
			isDragging = true;
			this.setCursor(dragCursor);
		}
		else
		{
			if (state==GAME)
			{
				if (!(g.currentPlayer instanceof AIPlayer))
				{							
					if (g.state==Game.PLAYING)
					{
						if (g.playing_state==Game.PLAYING_TILE)
						{
							if (mouseButton==RIGHT && nextTile!=null)
							{
								partialRotation = 0;
								nextTile.rotate();
								isRotating = true;
							}
							else if (mouseButton==LEFT && nextTile!=null)
							{
								int si = bmouseX() / IMGWIDTH;
								int sj = bmouseY() / IMGHEIGHT;
								if (b.isValidCoordinate(si,sj) && b.get(si,sj)==null && b.isValidLocation(si, sj, nextTile))
								{
									// set world coord info before placing
									g.placeTile(nextTile, si, sj);
									nextTile = null;						
								}
							}
						}
						else if (g.playing_state==Game.PLAYING_MEEPLE)
						{	
							// mouse button right cancels the placing of a meeple
							// mouse button left places a meeple
							if (mouseButton==LEFT)
							{
								// find meeple location
								Tile.MeepleLocation ml = g.lastTilePlaced().findClosestMeepleLocation(bmouseX(),bmouseY());
								if (ml!=null)
								{					
									if (g.canPlaceMeeple(ml))
									{
										g.placeMeeple(ml);
										g.endTurn();
									}
									else
									{
										// flash red or something?
									}
								}
								else
								{
									Debug.out.println("No meeple location selected. Right-click to skip meeple placement.");
								}
							}
							else if (mouseButton==RIGHT)
							{
								g.skipMeeple();
								g.endTurn();							
							}
						}
					}
				}
			}}
	}

	public void mouseDragged()
	{
		if (isDragging)
		{
			camX = mouseX - dragPointX;// - mouseX; // - dragPointX;
			camY = mouseY - dragPointY;// - mouseY; // - dragPointY;
		}
	}
	
	public void mouseMoved()
	{
		if (state==TITLE)
		{
			boolean handled = titleScreen.mouseMoved(mouseX,mouseY);
		}
	}

	public void mouseReleased()
	{
		if (isDragging)
		{
			isDragging = false;
			this.setCursor(defaultCursor);
		}
	}

	public void keyPressed()
	{
		if (g.state==Game.PLAYING)
		{
			if (keyCode==CONTROL)
			{
				setCursor(dragCursor);				
			}
			else
			{			
				if (g.playing_state==Game.PLAYING_MEEPLE)
				{
					if (key=='u') // pick up tile and revert state
					{
						nextTile = g.undoPlaceTile();
					}
				}
			}
		}
	}

	public void keyReleased()
	{
		setCursor(defaultCursor);
	}

	/* draws the tile with upper corner (x,y) 
	 * @param x
	 * @param y
	 */
	public void drawTile(Tile t, float x, float y)
	{
		pushMatrix();
		translate(x+IMGWIDTH/2.f,y+IMGHEIGHT/2.f);
		rotate(-t.getRotation()*PApplet.HALF_PI);		
		translate(-IMGWIDTH/2.f,-IMGHEIGHT/2.f);
		image(theme.getTileImage(t),0,0);
		popMatrix();				
	}

	// draw with a partial rotation
	// for animation
	public void drawTile(Tile t, float x, float y, float rot)
	{
		pushMatrix();
		translate(x+IMGWIDTH/2.f,y+IMGHEIGHT/2.f);
		rotate(-rot*PApplet.HALF_PI);
		translate(-IMGWIDTH/2.f,-IMGHEIGHT/2.f);
		image(theme.getTileImage(t),0,0);
		popMatrix();		
	}

	// draws the board
	public void drawBoard(Board b)
	{
		//fill(100,100,100);
		pushMatrix();
		//translate(PLAYERSIDEBARWIDTH,0);

		noStroke();
		fill(theme.backgroundColour(this));		
		rect(realToCamX(0),realToCamY(0),b.width*IMGWIDTH,b.height*IMGHEIGHT);

		noFill();
		stroke(0);

		// draw the shadow layer
		PImage shadow = theme.getTileShadowImage();
		for(int i=0;i<b.width;i++)
			for(int j=0;j<b.height;j++)
			{
				if (b.get(i,j)!=null)
				{
					image(shadow,boardToWorldX(i+0.5f)-shadow.width/2.f, boardToWorldY(j+0.5f) + 4 - shadow.height/2.f);
				}
			}

		// draw tiles
		for(int i=0;i<b.width;i++)
			for(int j=0;j<b.height;j++)
			{
				Tile t = b.get(i,j);
				if (t!=null) 
				{
					noTint();
					if (g.tilesOfInterestLastTurn.contains(t))
					{
						tint(200,200,200);						
					}

					//drawTile(t,t.x + t.tw/2, t.y + t.th/2);
					drawTile(t,realToCamX(t.x),realToCamY(t.y));
				}
			}

		// draw meeples
		for(int i=0;i<b.width;i++)
			for(int j=0;j<b.height;j++)
			{
				Tile t = b.get(i,j);
				if (t!=null)
				{
					for (Tile.MeepleLocation ml: t.meeple_locations)
					{
						float rx = realToCamX(ml.wx);
						float ry = realToCamY(ml.wy);

						if (DEBUGDRAW && t==g.lastTilePlaced() && g.playingState()==g.PLAYING_MEEPLE)
						{
							// only draw the meeple location iff the its global feature is available
							if (b.tryMeepleLocation(ml.feature) == true )
							{
								fill(0);
								rect(rx-2,ry-2,4,4);
							}
						}

						if (ml.meeple!=null)
						{
							PImage meep = theme.getMeepleImage(ml.meeple.owner.colour);
							image(meep,rx-meep.width/2.f,ry-meep.height/2.f);
						}
					}
				}			
			}

		popMatrix(); 	
	}

	public void drawPlayers()
	{

	}

	public void centerCamera()
	{
		camX = (int) (WIDTH/2.0 - b.width*IMGWIDTH/2.0);
		camY = (int) (HEIGHT/2.0 - b.height*IMGHEIGHT/2.0);
	}

	// mouse pos relative to the board
	public int bmouseX(){return mouseX - camX;}
	public int bmouseY(){return mouseY - camY;}

	public float boardToWorldX(float i){return i*IMGWIDTH + camX;}
	public float boardToWorldY(float j){return j*IMGHEIGHT + camY;}

	public float realToCamX(float x){return x+camX;}
	public float realToCamY(float y){return y+camY;}
}
